<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        /* 属性的简洁表达法 */
        const foo = 'bar';
        const baz = {
            foo,
            method() {
                return 'I am method';
            }
            // 不能用于构造函数
        };
        function f(x, y) {
            return { x, y }; // = {x: x, y: y}
        }
        console.log(baz, f(1, 2), baz.method());
        // 简洁打印对象
        {
            let user = { name: 'test' };
            let foo = { bar: 'baz' };
            console.log(user, foo)
            // {name: "test"} {bar: "baz"}
            console.log({ user, foo })
            // {user: {name: "test"}, foo: {bar: "baz"}}
        }

        /* 属性名表达式 */
        let propKey = 'foo';
        let obj = {
            [propKey]: 'I am foo',
            ['a' + 'bc']: 123,
            ['h' + 'ello']() {
                return 'hello!';
            }
        }
        console.log(obj, obj.hello());

        /* 方法的name属性 - 返回函数名*/
        console.log(obj.hello.name);
        /* 
            
            bind方法创造的函数，name属性返回bound加上原函数的名字；
            Function构造函数创造的函数，name属性返回anonymous。
            使用了取值函数（getter）和存值函数（setter），则name属性不是在该方法上面，
            而是该方法的属性的描述对象的get和set属性上面，返回值是方法名前加上get和set。
            对象的方法是一个 Symbol 值，
            那么name属性返回的是这个 Symbol 值的描述。
        */

        /* super 关键字 - 指向当前对象的原型对象 */
        let superType = {
            name: 'proto',
            foo() {
                console.log(this.name);
            }
        };
        let subType = {
            name: 'now',
            // 只能用在对象方法的简写中
            find() {
                console.log(super.name);
            },
            foo() {
                super.foo();
            }
        };
        Object.setPrototypeOf(subType, superType);
        subType.find(); // proto
        subType.foo();  // now
        // 指向原型对象的foo方法, 但this绑定的还是subType

        /* 对象的扩展运算符 */
        const o = Object.create({ x: 1, y: 2});
        o.z = 3;
        let { x, ...newObj } = o;
        let { y, z } = newObj;
        console.log(x, y, z, newObj);
        // 扩展运算符的解构赋值只能读取对象自身的属性, 不能读取继承的属性
        let rest = { ...'hello' };
        console.log(typeof rest);
        // 对象的扩展运算符等同于使用Object.assign()方法
        // 只拷贝对象实例的属性, 不拷贝对象原型的属性
        // 用户自定义的属性，放在扩展运算符后面，则扩展运算符内部的同名属性会被覆盖掉。
        // 用户自定义的属性，放在扩展运算符前面，就变成了设置新对象的默认属性值。
        let oClone1 = { ...o };
        let oClone2 = Object.assign({}, o);
        console.log(oClone1, oClone2);
        let oClone3 = { ...o, x: 2, y: 3};
        let oClone4 = { t: 4, ...o};
        console.log(oClone3, oClone4);
    </script>
</body>

</html>